18장. 같은 이벤트는 여러 번 온다 — 멱등성
이벤트는 중복될 수 있다.
그리고 이벤트는 순서대로 오지도 않는다.
이 두 가지가 합쳐지면
시스템은 쉽게 꼬인다.
같은 이벤트가 여러 번, 순서 없이 도착한다.
이번 장에서는
그 중 “중복” 문제를 다룬다.
왜 중복 이벤트가 발생하는가
이벤트 시스템은 보통
다음 전략을 선택한다.
최소 한 번(at-least-once) 전달
이 구조에서는
중복을 피할 수 없다.
1️⃣ ACK 실패
처리는 끝났지만 ACK이 전달되지 않으면
브로커는 같은 메시지를 다시 보낸다.
2️⃣ 재시도
Producer 또는 Broker가
전송 실패 시 재시도하면서 중복이 발생한다.
3️⃣ Consumer 재시작
Consumer가 재시작되면
이미 처리한 메시지를 다시 처리할 수 있다.
4️⃣ 외부 이벤트
Webhook 기반 시스템은
응답이 없으면 동일 이벤트를 재전송한다.
중복 이벤트가 만드는 문제
중복 이벤트를 그대로 처리하면
시스템은 쉽게 깨진다.
예:
- 결제가 2번 처리됨
- 포인트가 2번 적립됨
- 상태가 잘못 변경됨
즉,
같은 이벤트를 여러 번 처리해도
결과는 한 번 처리한 것과 같아야 한다.
멱등성이란 무엇인가
멱등성이란
동일한 요청을 여러 번 수행해도
결과가 변하지 않는 성질
이다.
브로커는 멱등성을 보장하지 않는다
일부 시스템은 멱등성을 지원하지만
그 범위는 제한적이다.
브로커는 중복을 줄여줄 뿐
비즈니스 중복까지 막아주지 않는다.
결국 멱등성은
애플리케이션의 책임이다.
애플리케이션 레벨 대응 전략
중복 문제의 본질은 이것이다.
이미 처리했는가?
1️⃣ 고유 ID 기반 처리
이벤트에 고유 ID를 두고
이미 처리한 ID는 무시한다.
장점:
- 가장 확실한 방법
단점:
- 저장소 관리 필요
2️⃣ 상태 기반 처리
현재 상태를 기준으로
이벤트를 적용할지 판단한다.
예:
- 이미 COMPLETED → 다시 처리하지 않음
장점:
- 추가 저장소 없이 가능
단점:
- 상태 설계에 의존
3️⃣ 조건부 업데이트
DB에서 조건을 걸어
중복 처리를 방지한다.
예:
UPDATE order
SET status = 'COMPLETED'
WHERE id = ? AND status != 'COMPLETED';
장점:
- DB에서 원자성 보장
단점:
- 복잡한 로직에는 한계
4️⃣ 멱등 키 사용
요청에 멱등 키를 포함하고
같은 키 요청은 한 번만 처리한다.
장점:
- API 레벨 제어 가능
단점:
- 클라이언트 협력 필요
5️⃣ 결과 재사용
처리 결과를 저장하고
중복 요청 시 재사용한다.
장점:
- 완전한 멱등성
단점:
- 저장 비용 증가
순서와 멱등성은 함께 깨진다
현실에서는 이 두 문제가 동시에 발생한다.
- 중복 이벤트 + 순서 역전
- 늦게 온 이벤트 + 이미 처리된 상태
따라서 설계 원칙은 이것이다.
순서가 깨지고 중복이 와도
결과는 항상 올바르게 유지되어야 한다.
멱등성은 선택이 아니라 전제다
이벤트 기반 시스템에서는
- 중복은 항상 발생한다
- 한 번만 처리된다는 보장은 없다
따라서
중복을 제거하려 하지 말고
중복이 와도 안전하게 만들어라
이 장의 핵심
이벤트 기반 시스템에서는
- 이벤트는 여러 번 전달된다
- 중복은 예외가 아니라 기본이다
- 브로커는 멱등성을 보장하지 않는다
- 멱등성은 애플리케이션의 책임이다
- 순서와 중복 문제는 함께 고려해야 한다